#include "Scheduler.h"
#include "Mailer.h"

Scheduler::Scheduler(int algo, vector<Node*> nodes, int quanta, Mailer* mailer) :
	OSScheduler(), _nodes(nodes), _quanta(quanta), _algo(algo) {
	_mailer = mailer;
	cout << "scheduler created" << endl;
	switch (_algo) {
	case 1:
		RRInit();
		break;
	default:
		break;
	}
}

Scheduler::~Scheduler() {

}

/*called from mailer, give time to node
 * change when needed
 * return: system is stable
 * */
bool Scheduler::Schedule() {
	//TODO: return boolean if stable state

	switch (_algo) {
	case 1:

		//			 if current node finished its run
		if (!_readyQ.empty()) {
			if ((_readyQ.front())->isStable()) {
				_readyQ.pop();

			}
			//			printQueue();

			if (Finished(_currentNode)) {
				RoundRobin();

			} else {
			}
		}
		//else do nothing
		break;
	default:
		break;
	}

	return isStable();
}

bool Scheduler::isStable() {
	bool yieldAns = true;
	bool stableAns = true;
	bool ans;
	for (int i = 0; i < _nodes.size(); i++) {
		if (!(_nodes[i])->isYield()) {
			ans = false;
		} else {
		}

		if (!(_nodes[i])->isStable()) {
			ans = false;
		} else {
		}
	}
	if (!_mailer->emptyInbox()) {
		ans = false;
	}
	if (ans && (yieldAns || stableAns)) {
		cout << "STABLE!!!!!!" << endl;
	}
	return ans;
}

// Initializing threads - RoundRobin
void Scheduler::RRInit() {
	_tt = new TT(_nodes);

	//updating TT
	//putting nodes in READY queue/status
	//	vector<Node*>::iterator it;
	//	for (it = _nodes.begin(); it < _nodes.end(); it++) {
	//		if ((Node*)it)->Active(){
	//			_readyQ.push(*it);
	//		}
	//	}
	for (int i = 0; i < _nodes.size(); ++i) {
//		if (_nodes.at(i)->IsActive()) {
			_readyQ.push(_nodes.at(i));
//		}
	}

	//putting first node in line
	_currentNode = _readyQ.front();
	_readyQ.pop();
	_currentNode->setQuanta(_quanta);
	_tt->setStatus(_currentNode, TT::RUNNING);
}

// resumes current thread and starts new one
void Scheduler::RoundRobin() {
	//stop and update current thread -- putting in READY queue
	_readyQ.push(_currentNode);
	_tt->setStatus(_currentNode, TT::READY);
	// select new thread
	_currentNode = _readyQ.front();
	_readyQ.pop();
	ContextSwitch(_currentNode);

}

//currently just for RR
void Scheduler::ContextSwitch(Node* newNode) {
	_tt->setStatus(newNode, TT::RUNNING);
	notifyThreadChange(newNode);
	//	_tt->Print();
	// let quanta to thread
	newNode->setQuanta(_quanta);
	ResetTTCount(newNode);
}

/*returns is n has finished running,
 * or just out of cpu time
 * */
bool Scheduler::Finished(Node *n) {
	bool leftQuanta = (n->getQuanta() > 0);
	bool blocked = n->isBlocked();
	bool yielded = n->isYield();

	return !leftQuanta || blocked || yielded;

}

void Scheduler::printQueue() {
	//	queue<Node*> newQueue =  Queue(_readyQ);

	cout << "PRINTING QUEUE!!!!" << endl;
	cout << "___________________" << endl;
	for (int i = 0; i < _readyQ.size(); i++) {
		Node* n = _readyQ.front();
		cout << ": " << n->getId();
		_readyQ.pop();
		_readyQ.push(n);
	}
	cout << endl << endl;

}

void Scheduler::UpdateNewReadyQ(queue<Node*> nq){
	_readyQ = nq;
}
void Scheduler::ResetTTCount(Node* n) {
	_tt->ResetStage(n);
}

void Scheduler::IncTT(Node* n) {
	_tt->IncCurrentStageQuanta(n);
}
void Scheduler::PrintTT() {
	_tt->Print();
}
